#include "rendColor.hpp"
#include <algorithm>

namespace rend
{

const Color Color::Black	= Color(0.f, 0.f, 0.f);
const Color Color::White	= Color(1.f, 1.f, 1.f);
const Color Color::Red		= Color(1.f, 0.f, 0.f);
const Color Color::Green	= Color(0.f, 1.f, 0.f);
const Color Color::Blue		= Color(0.f, 0.f, 1.f);

const ColorA ColorA::Black	= ColorA(0.f, 0.f, 0.f);
const ColorA ColorA::White	= ColorA(1.f, 1.f, 1.f);
const ColorA ColorA::Red	= ColorA(1.f, 0.f, 0.f);
const ColorA ColorA::Green	= ColorA(0.f, 1.f, 0.f);
const ColorA ColorA::Blue	= ColorA(0.f, 0.f, 1.f);


template < class COLOR_RGBX_T >
inline void setHSB(COLOR_RGBX_T& color, float hue, float saturation, float brightness)
{
	// wrap hue
	if (hue > 1.0f)
		hue -= (int)hue;
	else if (hue < 0.0f)
		hue += (int)hue + 1;

	// clamp saturation / brightness
	saturation = std::min(saturation, 1.f);
	saturation = std::max(saturation, 0.f);
	brightness = std::min(brightness, 1.f);
	brightness = std::max(brightness, 0.f);

	if (brightness == 0.f)
	{   
		// early exit, this has to be black
		color.setR(0.f);
		color.setG(0.f);
		color.setB(0.f);
		return;
	}

	if (saturation == 0.f)
	{   
		// early exit, this has to be grey
		color.setR(brightness);
		color.setG(brightness);
		color.setB(brightness);
		return;
	}

	float hueDomain = hue * 6.f;
	if (hueDomain >= 6.f)
	{
		// wrap around, and allow mathematical errors
		hueDomain = 0.f;
	}
	const unsigned short domain = (unsigned short)hueDomain;
	const float f1 = brightness * (1 - saturation);
	const float f2 = brightness * (1 - saturation * (hueDomain - domain));
	const float f3 = brightness * (1 - saturation * (1 - (hueDomain - domain)));

	switch (domain)
	{
	case 0:
		// red domain; green ascends
		color.setR(brightness);
		color.setG(f3);
		color.setB(f1);
		break;
	case 1:
		// yellow domain; red descends
		color.setR(f2);
		color.setG(brightness);
		color.setB(f1);
		break;
	case 2:
		// green domain; blue ascends
		color.setR(f1);
		color.setG(brightness);
		color.setB(f3);
		break;
	case 3:
		// cyan domain; green descends
		color.setR(f1);
		color.setG(f2);
		color.setB(brightness);
		break;
	case 4:
		// blue domain; red ascends
		color.setR(f3);
		color.setG(f1);
		color.setB(brightness);
		break;
	case 5:
		// magenta domain; blue descends
		color.setR(brightness);
		color.setG(f1);
		color.setB(f2);
		break;
	}
}


Color& Color::setAsHSB(float hue, float saturation, float brightness)
{
	setHSB< Color >(*this, hue, saturation, brightness);
	return *this;
}


ColorA& ColorA::setAsHSB(float hue, float saturation, float brightness)
{
	setHSB< ColorA >(*this, hue, saturation, brightness);
	return *this;
}

} // namespace rend
